#include "Database.h"
#include <regex>

using namespace std;

#define MSG_NOERR_QUERY "Execution complete."
#define MSG_ERR_QUERY_UNKNOWNCOMMAND "Unknown Command."

static regex insert_string_match("insert (\\('([^']+)'\\s*,\\s*'([^']+)'\\s*,\\s*'([^']+)'\\s*\\))(,\\s*\\(\\s*'([^']+)'\\s*,\\s*'([^']+)'\\s*,\\s*'([^']+)'\\s*\\))*\\s*;?", regex_constants::icase);
static regex insert_sub_string_match("\\('([^']+)'\\s*,\\s*'([^']+)'\\s*,\\s*'([^']+)'\\s*\\)", regex_constants::icase);
static regex select_string_match("select all;?", regex_constants::icase);
static regex find_string_match("select \\('([^']*)'\\s*,\\s*'([^']*)'\\s*,\\s*'([^']*)'\\s*\\);?", regex_constants::icase);
static regex delete_string_match("delete \\('([^']*)'\\s*,\\s*'([^']*)'\\s*,\\s*'([^']*)'\\s*\\);?", regex_constants::icase);
static regex update_string_match("update \\('([^']*)'\\s*,\\s*'([^']*)'\\s*,\\s*'([^']*)'\\s*\\) set \\('([^']*)'\\s*,\\s*'([^']*)'\\s*,\\s*'([^']*)'\\s*\\);?", regex_constants::icase);
static regex create_string_match("create\\s*([0-9a-zA-Z_]+);?", regex_constants::icase);
static regex use_string_match("use\\s*([0-9a-zA-Z_]+);?", regex_constants::icase);
static regex import_string_match("import\\s*([^']+);?", regex_constants::icase);
static regex import_sub_string_match("\"([^\"]+)\"\\s*,\\s*\"([^\"]+)\"\\s*,\\s*\"([^\"]+)\"\\s*", regex_constants::icase);
static regex commit_string_match("commit\\s*;?", regex_constants::icase);

Database::Database() {
}


Database::~Database() {
}

void Database::create(const string& database)  {
    DiscManager::createNewDatabase(database);
}

void Database::open(const string& database) {
    DiscManager::useDatabase(database);
}


QueryResult Database::executeQuery(string query) {

    if (query == "exit")
        return QueryResult("Tschuess");

    smatch match_result;
    if (regex_search(query, match_result, insert_string_match)){
        if (regex_search(query, match_result, insert_sub_string_match))
        {
            const std::sregex_token_iterator End;
            int submatches[] = { 1, 2, 3 };
            std::sregex_token_iterator i(query.begin(), query.end(), insert_sub_string_match, submatches);
            while (i != End)
            {
                string subjData = *i;
                i++;
                string praedData = *i;
                i++;
                string objData = *i;
                i++;
                Database::insert(subjData, praedData, objData);
            }
        }
        return QueryResult(MSG_NOERR_QUERY);
    }

    if (regex_search(query, match_result, select_string_match)) {
        string graph = match_result[1];
        return Database::graphScan();
    }

    if (regex_search(query, match_result, find_string_match)) {
        string subjData = match_result[1];
        string praedData = match_result[2];
        string objData = match_result[3];
        return Database::findScan(subjData, praedData, objData);
    }

    if (regex_search(query, match_result, delete_string_match)) {
        string subjData = match_result[1];
        string praedData = match_result[2];
        string objData = match_result[3];
        Database::deleteQuery(subjData, praedData, objData);
        return QueryResult(MSG_NOERR_QUERY);
    }

    if (regex_search(query, match_result, update_string_match)) {
        string oldsubjData = match_result[1];
        string oldpraedData = match_result[2];
        string oldobjData = match_result[3];
        string newsubjData = match_result[4];
        string newpraedData = match_result[5];
        string newobjData = match_result[6];

        Database::updateQuery(oldsubjData, oldpraedData, oldobjData, newsubjData, newpraedData, newobjData);
        return QueryResult(MSG_NOERR_QUERY);
    }

    if (regex_search(query, match_result, create_string_match)) {
        string database = match_result[1];
        Database::create(database);
        return QueryResult(MSG_NOERR_QUERY);
    }

    if (regex_search(query, match_result, use_string_match)) {
        string database = match_result[1];
        Database::open(database);
        return QueryResult(MSG_NOERR_QUERY);
    }

    if (regex_search(query, match_result, import_string_match)) {
        string file = match_result[1];
        Database::import(file);
        return QueryResult(MSG_NOERR_QUERY);
    }

    if (regex_search(query, match_result, commit_string_match)) {
        BufferManager::commitChanges();
        return QueryResult(MSG_NOERR_QUERY);
    }

    return QueryResult(MSG_ERR_QUERY_UNKNOWNCOMMAND);
}


void Database::insert(const string& subject, const string& praedicat, const string& object) {
    TripelManager::newTripel(sTripel{ subject, praedicat, object });
}


void Database::import(const string& file){
    string temp;
    fstream f;
    f.open(file, ios::in);
    while (!f.eof())
    {
        string line;
        getline(f, line);

        const std::sregex_token_iterator End;
        int submatches[] = { 1, 2, 3 };
        std::sregex_token_iterator i(line.begin(), line.end(), import_sub_string_match, submatches);
        while (i != End)
        {
            string subject = *i;
            i++;
            string praedicat = *i;
            i++;
            string object = *i;
            i++;

            TripelManager::newTripel(sTripel{ subject, praedicat, object });
        }
    }
    f.close();
}

void Database::deleteQuery(const string& subject, const string& praedicat, const string& object) {
    TripelManager::removeTripel(sTripel{ subject, praedicat, object });
}

void Database::updateQuery(const string& oldsubject, const string& oldpraedicat, const string& oldobject, const string& newsubject, const string& newpraedicat, const string& newobject) {
    sTripel oldtrip = sTripel{ oldsubject, oldpraedicat, oldobject };
    sTripel newtrip = sTripel{ newsubject, newpraedicat, newobject };
    TripelManager::updateTripel(oldtrip, newtrip);
}

QueryResult Database::findScan(const string& subject, const string& praedicat, const string& object) {
    sTripel findTripel{ subject, praedicat, object };

    return FindScanQueryResult(findTripel, MSG_NOERR_QUERY);
}

QueryResult Database::graphScan() {
    return FullScanQueryResult(MSG_NOERR_QUERY);
}
